// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//
#pragma once

#include <pollux/common/hyperloglog/dense_hll.h>
#include <pollux/common/hyperloglog/hll_utils.h>
#include <pollux/common/hyperloglog/sparse_hll.h>
#include <pollux/functions/macros.h>
#include <pollux/functions/prestosql/types/hyper_log_log_type.h>

namespace kumo::pollux::functions {
    template<typename T>
    struct CardinalityFunction {
        POLLUX_DEFINE_FUNCTION_TYPES(T);

        MELON_ALWAYS_INLINE bool call(
            int64_t &result,
            const arg_type<HyperLogLog> &hll) {
            using common::hll::DenseHll;
            using common::hll::SparseHll;

            if (SparseHll::canDeserialize(hll.data())) {
                result = SparseHll::cardinality(hll.data());
            } else {
                result = DenseHll::cardinality(hll.data());
            }
            return true;
        }
    };

    template<typename T>
    struct EmptyApproxSetFunction {
        POLLUX_DEFINE_FUNCTION_TYPES(T);

        MELON_ALWAYS_INLINE bool call(out_type<HyperLogLog> &result) {
            static const std::string kEmpty =
                    common::hll::SparseHll::serializeEmpty(12);

            result.resize(kEmpty.size());
            memcpy(result.data(), kEmpty.data(), kEmpty.size());
            return true;
        }
    };

    template<typename T>
    struct EmptyApproxSetWithMaxErrorFunction {
        POLLUX_DEFINE_FUNCTION_TYPES(T);
        std::string serialized_;

        MELON_ALWAYS_INLINE void initialize(
            const std::vector<TypePtr> & /*inputTypes*/,
            const core::QueryConfig & /*config*/,
            const double *maxStandardError) {
            POLLUX_USER_CHECK_NOT_NULL(
                maxStandardError,
                "empty_approx_set function requires constant value for maxStandardError argument");
            common::hll::checkMaxStandardError(*maxStandardError);
            serialized_ = common::hll::SparseHll::serializeEmpty(
                common::hll::toIndexBitLength(*maxStandardError));
        }

        MELON_ALWAYS_INLINE bool call(
            out_type<HyperLogLog> &result,
            double /*maxStandardError*/) {
            result.resize(serialized_.size());
            memcpy(result.data(), serialized_.data(), serialized_.size());
            return true;
        }
    };
} // namespace kumo::pollux::functions
